﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
using System.Windows;
using System.Net;
using Microsoft.Win32;
using System.Diagnostics;
using System.IO;
using LitJson;

namespace MyWcfRestClient
{
    class ServiceInfo
    {
        public const string DefaultServiceAddress = "http://localhost:8231/files";
        private const string ServiceInfoApi = "/get_service_info";
        private const string UploadApi = "/file?root={0}&path={1}";
        private const string DownloadApi = "/file?root={0}&path={1}";
        private const string ChunkUploadApi = "/chunked_upload?upload_id={0}&offset={1}";
        private const string CommitChunkApi = "/commit_chunked_upload";

        // BaseAddress, must be set
        public string BaseAddress { get; set; }

        public string ServiceInfoAddress { get { return BaseAddress + ServiceInfoApi; } }
        public string UploadAddress { get { return BaseAddress + UploadApi; } }
        public string DownloadAddress { get { return BaseAddress + DownloadApi; } }
        public string ChunkUploadAddress { get { return BaseAddress + ChunkUploadApi; } }
        public string CommitChunkAddress { get { return BaseAddress + CommitChunkApi; } }

        public int BufferSize { get; set; }

        public ServiceInfo(string serviceAddres)
        {
            BaseAddress = serviceAddres;
        }

    }

    internal class UploadFileViewModel : ViewModelBase
    {
        private bool isUploading = false;
        private bool isDownloading = false;

        private string deviceName = "DefaultDevice";

        public ICommand GetServiceInfoCommand
        {
            get
            {
                return new GenericCommand
                {
                    ExecuteCallback = delegate
                    {
                        GetServiceInfo();
                    }
                };
            }
        }

        public ICommand UploadCommand
        {
            get
            {
                return new GenericCommand
                {
                    ExecuteCallback = delegate
                    {
                        UploadFile();
                    },
                    CanExecuteCallback = delegate
                    {
                        return (!isUploading);
                    }
                };
            }
        }

        public ICommand DownloadCommand
        {
            get
            {
                return new GenericCommand
                {
                    ExecuteCallback = delegate
                    {
                        DownloadFile();
                    },
                    CanExecuteCallback = delegate
                    {
                        return (!isUploading);
                    }
                };
            }
        }

        private string outputString;
        public string OutputString
        {
            get
            {
                return outputString;
            }
            set
            {
                if (value != outputString)
                {
                    outputString = value;
                    ReportPropertyChanged("OutputString");
                }
            }
        }

        private ServiceInfo myServiceInfo = new ServiceInfo(ServiceInfo.DefaultServiceAddress);
        public string ServiceBaseAddress
        {
            get
            {
                if (null == myServiceInfo)
                    return ServiceInfo.DefaultServiceAddress;
                else
                    return myServiceInfo.BaseAddress;
            }
            set
            {
                if (value != myServiceInfo.BaseAddress)
                {
                    myServiceInfo.BaseAddress = value;
                    ReportPropertyChanged("ServiceBaseAddress");
                }
            }
        }

        private string uploadToPath = "";
        public string UploadToPath
        {
            get
            {
                return uploadToPath;
            }
            set
            {
                if (value != uploadToPath)
                {
                    uploadToPath = value;
                    ReportPropertyChanged("UploadToPath");
                }
            }
        }

        private void GetServiceInfo()
        {
            if (null == myServiceInfo)
            {
                myServiceInfo = new ServiceInfo(ServiceInfo.DefaultServiceAddress);
            }

            try
            {
                HttpHelper myHttpHelper = new HttpHelper();
                HttpWebResponse response = myHttpHelper.GetRequest(myServiceInfo.ServiceInfoAddress);
                string responseString = myHttpHelper.GetResponseContentString(response);
                response.Close();
                JsonData jsonData = JsonMapper.ToObject(responseString);
                myServiceInfo.BufferSize = (int)jsonData["max_message_size"];
                OutputString = string.Format("Succeed!\nRequest Uri: {0}\nMax Chunk Size is {1} KB.", myServiceInfo.ServiceInfoAddress, myServiceInfo.BufferSize / 1024);
            }
            catch (Exception ex)
            {
                OutputString = string.Format("Failed!\nRequest Uri: {0}\n{1}", myServiceInfo.ServiceInfoAddress, ex.Message);
            }

        }

        private void UploadFile()
        {
            isUploading = true;

            GetServiceInfo();
            int messageSize = myServiceInfo.BufferSize;

            OpenFileDialog op = new OpenFileDialog();
            op.Filter = "All files(*.*)|*.*";
            if (true == op.ShowDialog())
            {
                System.IO.FileStream fs = new System.IO.FileStream(op.FileName, System.IO.FileMode.Open);
                string fileName = Path.Combine(UploadToPath, Path.GetFileName(op.FileName));
                if (fs.Length > messageSize)
                {
                    UploadBigFile(fs, fileName, messageSize);
                }
                else
                {
                    UploadSmallFile(fs, fileName);
                }
            }
            isUploading = false;

        }

        private void UploadSmallFile(FileStream file, string filename)
        {
            using (WebClient uploadClient = new WebClient())
            {
                uploadClient.Headers.Add("Content-Type", ".*");
                byte[] buffer = new byte[(int)file.Length];
                file.Read(buffer, 0, (int)file.Length);
                file.Close();
                try
                {
                    uploadClient.UploadData(new Uri(string.Format(myServiceInfo.UploadAddress, deviceName, filename)), buffer);
                }
                catch (WebException webException)
                {
                    string message = webException.Message;
                    Debug.Print(message);
                }
            }
        }

        private void UploadBigFile(FileStream file, string filename, int chunkSize)
        {
            using (WebClient uploadClient = new WebClient())
            {
                uploadClient.Headers.Add("Content-Type", ".*");
                byte[] buffer = new byte[chunkSize];
                int byteReads = file.Read(buffer, 0, chunkSize);
                try
                {
                    int offset = 0;
                    HttpHelper myHttpHelper = new HttpHelper();
                    WebResponse response = myHttpHelper.PostBinaryData(string.Format(myServiceInfo.ChunkUploadAddress, "", 0), buffer, byteReads);
                    string responseString = myHttpHelper.GetResponseContentString(response);
                    JsonData jsonData = JsonMapper.ToObject(responseString);
                    string upload_id = (string)jsonData["upload_id"];
                    response.Close();
                    offset += byteReads;

                    byteReads = file.Read(buffer, 0, chunkSize);
                    while (byteReads > 0)
                    {
                        response = myHttpHelper.PostBinaryData(string.Format(myServiceInfo.ChunkUploadAddress, upload_id, offset), buffer, byteReads);
                        responseString = myHttpHelper.GetResponseContentString(response);
                        jsonData = JsonMapper.ToObject(responseString);
                        upload_id = (string)jsonData["upload_id"];
                        response.Close();
                        offset += byteReads;

                        byteReads = file.Read(buffer, 0, chunkSize);
                    }

                    // Commit Chunk Upload
                    JsonData commitChunkInfo = new JsonData();
                    commitChunkInfo["upload_id"] = upload_id;
                    commitChunkInfo["root"] = deviceName;
                    commitChunkInfo["path"] = filename;
                    response = myHttpHelper.PostTextData(myServiceInfo.CommitChunkAddress, commitChunkInfo.ToJson());
                    responseString = myHttpHelper.GetResponseContentString(response);
                    jsonData = JsonMapper.ToObject(responseString);
                    response.Close();

                }
                catch (WebException webException)
                {
                    string message = webException.Message;
                    Debug.Print(message);
                }
                file.Close();
            }

        }

        private void DownloadFile()
        {
            isDownloading = true;
            WebClient uploadClient = new WebClient();
            try
            {
                byte[] buffer = uploadClient.DownloadData(string.Format(myServiceInfo.DownloadAddress, deviceName, "test.file"));
                FileStream file = new FileStream("C:\\temp\\1.tmp", FileMode.Create, FileAccess.Write, FileShare.None);
                file.Write(buffer, 0, buffer.Length);
                file.Close();
            }
            catch (WebException webException)
            {
                string message = webException.Message;
                Debug.Print(message);
            }
            isDownloading = false;

        }
    }
}
